/*
iverilog -g2012 -o "d:/Git_data/FPGA_DigitalIDE/vending_machine_v2/prj/simulation/icarus/out1.vvp" -s vending_machine_mix1_tb -I "d:/Git_data/FPGA_DigitalIDE/vending_machine_v2/user/src/" "d:/Git_data/FPGA_DigitalIDE/vending_machine_v2/user/sim/vending_machine_mix1_tb.v"
vvp "d:/Git_data/FPGA_DigitalIDE/vending_machine_v2/prj/simulation/icarus/out1.vvp"
 
iverilog -g2012 -o "/Users/tan_haozun/Documents/Git_data_mac/FPGA_DigitalIDE/vending_machine_v2/prj/simulation/icarus/out1.vvp" -s vending_machine_mix1_tb -I "/Users/tan_haozun/Documents/Git_data_mac/FPGA_DigitalIDE/vending_machine_v2/user/src/" "/Users/tan_haozun/Documents/Git_data_mac/FPGA_DigitalIDE/vending_machine_v2/user/sim/vending_machine_mix1_tb.v"
vvp "/Users/tan_haozun/Documents/Git_data_mac/FPGA_DigitalIDE/vending_machine_v2/prj/simulation/icarus/out1.vvp"
*/

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/16 09:29:59
// Design Name:
// Module Name: vending_machine
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////

module vending_machine_mix1(
    // 输入信号
    input clk,                // 时钟信号，用于同步操作
    input rst,                // 复位信号，用于将机器状态恢复到初始状态
    input [2:0] coin,         // 投币输入，3位二进制表示不同面值的硬币（001: 1元； 010: 2元； 100: 5元）
    input [1:0] sel,          // 商品选择输入，2位二进制表示选择不同的商品

    // 输出信号
    output reg dispense,      // 商品输出信号，高电平表示正在出货
    output reg [4:0] balance, // 当前余额显示，5位二进制表示余额（最大31元）
    output reg [4:0] change,  // 找零输出，5位二进制表示找零金额
    output reg low_balance    // 余额不足信号，高电平表示余额不足
);

    // 商品价格参数
    parameter PRICE_A = 4;  // 商品A的价格是4元
    parameter PRICE_B = 6;  // 商品B的价格是6元
    parameter PRICE_C = 8;  // 商品C的价格是8元

    // 状态编码（使用独热码表示）
    parameter IDLE = 5'b00001;           // 空闲状态，等待用户操作
    parameter COLLECT = 5'b00010;        // 投币收集状态，正在接收用户投币
    parameter CHECK_BALANCE = 5'b00100;  // 余额检查状态，检查余额是否足够购买商品
    parameter REFUND = 5'b01000;         // 退款状态，退还用户余额
    parameter OUTPUT = 5'b10000;         // 出货状态，正在出货商品

    reg [4:0] state, next_state;    // 当前状态和下一个状态的寄存器
    reg [4:0] timer;                // 计时器，用于在COLLECT状态后返回IDLE状态

    // 时序逻辑部分，用于状态更新
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            // 当复位信号有效时，将状态和下一个状态设置为IDLE
            state <= IDLE;
            next_state <= IDLE;
        end else begin
            // 否则，将当前状态更新为下一个状态
            state <= next_state;
        end
    end

    // 组合逻辑部分，用于状态转换逻辑
    always @(*) begin
        case (state)
            IDLE:
                // 在空闲状态，如果检测到有硬币投入，则进入COLLECT状态
                if (coin == 3'b001 || coin == 3'b010 || coin == 3'b100) begin
                    next_state = COLLECT;
                end else begin
                    next_state = IDLE;
                end
            COLLECT:
                // 在收集状态，如果计时器达到20，则进入REFUND状态
                if (timer >= 5'd20 || balance >= 31) begin
                    next_state = REFUND;
                end else if (sel) begin
                    // 如果有商品选择，则进入CHECK_BALANCE状态
                    next_state = CHECK_BALANCE;
                end else begin
                    next_state = COLLECT;
                end
            CHECK_BALANCE:
                // 在余额检查状态，根据选择和余额决定下一步状态
                case (sel)
                    2'b01:
                        if (balance < PRICE_A) begin
                            next_state = REFUND;
                        end else begin
                            next_state = OUTPUT;
                        end
                    2'b10:
                        if (balance < PRICE_B) begin
                            next_state = REFUND;
                        end else begin
                            next_state = OUTPUT;
                        end
                    2'b11:
                        if (balance < PRICE_C) begin
                            next_state = REFUND;
                        end else begin
                            next_state = OUTPUT;
                        end
                endcase
            REFUND:
                // 在退款状态，下一步状态为IDLE
                next_state = IDLE;
            OUTPUT:
                // 在出货状态，下一步状态为IDLE
                next_state = IDLE;
        endcase
    end


// 定义一个5位寄存器用于存储商品价格
reg [4:0] price;

// 商品处理及余额更新的时序逻辑块
// 该块在时钟信号上升沿或复位信号上升沿触发
always @(posedge clk or posedge rst) begin
    if (rst) begin
        // 如果复位信号有效，将所有输出和内部变量初始化为默认值
        dispense <= 1'd0; // 商品输出信号置为0，表示无商品出货
        balance <= 5'd0; // 当前余额显示置为0
        change <= 5'd0; // 找零输出置为0
        low_balance <= 1'd0; // 余额不足标志置为0
        timer <= 5'd0; // 计时器置为0
        price <= 5'd0; // 商品价格置为0
    end
    else begin
        // 根据当前状态执行相应的操作
        case (state)
            IDLE: begin
                // 空闲状态：初始化输出和内部变量
                change <= 5'd0; // 找零置为0
                balance <= 5'd0; // 余额置为0
                dispense <= 0; // 商品输出信号置为0
                low_balance <= 0; // 余额不足标志置为0
                price <= 5'd0; // 商品价格置为0
                timer <= 5'd0; // 计时器置为0

                // 根据投入的硬币更新余额
                if (coin == 3'b001) // 如果投入1元
                    balance <= balance + 1; // 余额加1
                else if (coin == 3'b010) // 如果投入2元
                    balance <= balance + 2; // 余额加2
                else if (coin == 3'b100) // 如果投入5元
                    balance <= balance + 5; // 余额加5
                else
                    balance <= balance; // 否则余额保持不变
            end
            COLLECT: begin
                // 收集状态：增加计时器计数，更新余额和商品价格
                timer <= timer + 1; // 计时器加1

                // 根据投入的硬币更新余额，确保余额不超过最大值31
                case (coin)
                    3'b001: // 如果投入1元
                        balance <= (balance + 1 > 31) ? 31 : (balance + 1); // 余额加1，不超过31
                    3'b010: // 如果投入2元
                        balance <= (balance + 2 > 31) ? 31 : (balance + 2); // 余额加2，不超过31
                    3'b100: // 如果投入5元
                        balance <= (balance + 5 > 31) ? 31 : (balance + 5); // 余额加5，不超过31
                    default:
                        balance <= balance; // 否则余额保持不变
                endcase

                // 根据商品选择设置商品价格
                case (sel)
                    2'b01: // 如果选择商品1
                        price <= 5'd4; // 设置价格为4
                    2'b10: // 如果选择商品2
                        price <= 5'd6; // 设置价格为6
                    2'b11: // 如果选择商品3
                        price <= 5'd8; // 设置价格为8
                    default:
                        price <= 5'd0; // 否则价格置为0
                endcase
            end
            CHECK_BALANCE: begin
                // 余额检查状态：当前状态下不执行任何操作
                // 此状态可能用于等待其他操作或检查
            end
            REFUND: begin
                // 退款状态：设置找零为当前余额，重置其他输出信号
                change <= balance; // 找零设置为当前余额
                dispense <= 0; // 商品输出信号置为0
                balance <= 5'd0; // 余额置为0
                low_balance <= 1; // 余额不足标志置为1
            end
            OUTPUT: begin
                // 出货状态：计算找零，设置出货信号，重置余额和余额不足信号
                change <= balance - price; // 计算找零
                dispense <= 1; // 商品输出信号置为1，表示出货
                balance <= 5'd0; // 余额置为0
                low_balance <= 0; // 余额不足标志置为0
            end
        endcase
    end
end

endmodule